/*==================================================
 *  Exhibit.ExhibitJSONXImporter
 * Ensemble enhancement to JSON importer, adding ex:converter support to
 * allow items to be manipulated during database loading.
 *==================================================
 */
 
Exhibit.ExhibitJSONXImporter = {
};
Exhibit.importers["application/jsonx"] = Exhibit.ExhibitJSONXImporter;

Exhibit.ExhibitJSONXImporter.load = function(link, database, cont , fConvert) {
    /*var url = typeof link == "string" ? link : link.href;
    url = Exhibit.Persistence.resolveURL(url);*/

    function extractFunc(fConvert) {
        if (typeof fConvert == "string") {
        // Exhibit.ExhibitJSONXImporter.sparqlToJson ==> exhibit.ExhibitJSONXImporter.sparqlToJsonConverter
        var name = fConvert;
            name = name.charAt(0).toLowerCase() + name.substring(1) + "Converter";
            if (name in Exhibit.JSONPImporter) {
                return Exhibit.JSONPImporter[name];
            } else {
                try { return eval(fConvert); } catch (e) { return null; /* silent */ }
            }
        }
    }
 
    var url = link;
    var preConvertFunc = null;
    var postConvertFunc = null;
    if (typeof link != "string") {
        url = Exhibit.Persistence.resolveURL(link.href);  // href
        fConvert = Exhibit.getAttribute(link, "converter"); // ex:converter
        preConvertFunc = Exhibit.getAttribute(link, "preconverter"); // ex:preconverter
        postConvertFunc = Exhibit.getAttribute(link, "postconverter"); // ex:postconverter
   }
    fConvert = extractFunc(fConvert);
    preConvertFunc = extractFunc(preConvertFunc);
    postConvertFunc = extractFunc(postConvertFunc);
    // Call preprocessURL() if exists in converter
    if (fConvert != null && "preprocessURL" in fConvert) {
        url = fConvert.preprocessURL(url);
    }
	
	var filter = function(json,filter) {
		var items = json.items;
		
		// If JSON not in Exhibit format, leave.
		if(items == undefined) { return json; } 
		
		// Loop over each <xxx ex:role="filter"> in HTML
		$('[ex\\:role="'+filter+'"]').each(function(idx) {
			//var elem = $(this).get();
			
			// Extract filter params from HTML
			var type = $(this).attr("ex:itemType");
			var property = $(this).attr("ex:property");
			var value = $(this).attr("ex:value");
			var valueRE = (value!=undefined) ? new RegExp(value,"g") : undefined;
			//console.log("type="+type+" property="+property+" value="+value);
			
			// Loop over each item in json
			for(var i=0;i<items.length;i++) {
				var it = items[i];
				if(it==undefined) continue;
				
				var v = eval("it."+property);  // Extract value for property
				var accept = true;
				if(type!=undefined) { accept = accept && (type==it.type) };		// Type matches?
				if(property!=undefined) { accept = accept && (property in it) };	// Property exists?
				if((property!=undefined)&&(valueRE!=undefined)&&(v!=undefined)) { accept = accept && valueRE.test(v) };  // Property value matches?
				if(!accept) {
					//console.log("Delete "+it.label);
					delete items[i];
				}
			}
		});
		
		// Clean out all the undefined values caused by delete
		var ret = new Array();
		for(var i=0;i<items.length;i++) {
			if(items[i]!=undefined)  ret.push(items[i]);
		}
		json.items = ret;
		return json;
	}

    var fError = function(statusText, status, xmlhttp) {
        Exhibit.UI.hideBusyIndicator();
        Exhibit.UI.showHelp(Exhibit.l10n.failedToLoadDataFileMessage(url));
        if (cont) cont();
    };
    
    var fDone = function(xmlhttp) {
        Exhibit.UI.hideBusyIndicator();
        try {
            var o = null;
            try {
                o = eval("(" + xmlhttp.responseText + ")");
            } catch (e) {
                Exhibit.UI.showJsonFileValidation(Exhibit.l10n.badJsonMessage(url, e), url);
            }
            
            if (o != null) {
				o = preConvertFunc ? preConvertFunc(o, url, link) : o;  // Call convert callback
				o = filter(o,"preFilter");  // Perform filtering
				o = fConvert ? fConvert(o, url, link) : o;  // Call convert callback
				o = filter(o,"postFilter");  // Perform filtering
				o = postConvertFunc ? postConvertFunc(o, url, link) : o;  // Call convert callback
				database.loadData(o, Exhibit.Persistence.getBaseURL(url));
            }
        } catch (e) {
            SimileAjax.Debug.exception(e, "Error loading Exhibit JSON data from " + url);
        } finally {
            if (cont) cont();
        }
    };

    Exhibit.UI.showBusyIndicator();
    SimileAjax.XmlHttp.get(url, fError, fDone);
};

Exhibit.ExhibitJSONXImporter.sparqlToJson = function(ipJson,url,link) {
	var propertiesMap = new Array(); 
	propertiesMap["http://purl.org/dc/elements/1.1/description"] = "desc"; 
	propertiesMap["http://purl.org/dc/elements/1.1/format"] = "format";
	propertiesMap["http://purl.org/dc/elements/1.1/language"] = "lang";
	propertiesMap["http://purl.org/dc/elements/1.1/subject"] = "subj";
	propertiesMap["http://purl.org/dc/elements/1.1/coverage"] = "cov";
	propertiesMap["http://purl.org/dc/elements/1.1/type"] = "dc_type";
	propertiesMap["http://purl.org/dc/elements/1.1/creator"] = "creator";
	propertiesMap["http://purl.org/dc/elements/1.1/date"] = "date";
	propertiesMap["http://purl.org/dc/elements/1.1/contributor"] = "rights"; 
	propertiesMap["http://purl.org/dc/elements/1.1/contributor"] = "contr"; 
	propertiesMap["http://purl.org/dc/elements/1.1/publisher"] = "pub"; 
	propertiesMap["http://example.org/ddi2/samplingProcedure"] = "samp";
	propertiesMap["http://example.org/ddi2/universe"] = "univ";
	propertiesMap["http://example.org/ddi2/timeMethod"] = "time";
	propertiesMap["http://example.org/ddi2/dataCollector"] = "dataCol";
	propertiesMap["http://example.org/ddi2/collectionMode"] = "colMode";
	propertiesMap["http://www.w3.org/2004/02/skos/core/note"] = "note";
	propertiesMap["http://www.w3.org/2003/01/geo/wgs84_pos#lat_long"] = "lat";
	propertiesMap["http://www.w3.org/2003/01/geo/wgs84_pos#location"] = "loc";
	propertiesMap["http://example.org/qudex/labelCode"] = "label_code";
	propertiesMap["http://example.org/qudex/labelMemo"] = "label_memo";
	propertiesMap["http://example.org/qudex/memo"] = "memo";
	propertiesMap["info:fedora/fedora-system:def/relations-external#hasAnnotation"] = "hasAnnotation";
	propertiesMap["http://example.org/qudex/category"] = "category";
	propertiesMap["http://example.org/qudex/isDocumentationOf"] = "isDocumentationOf";
	propertiesMap["http://example.org/qudex/isAnnotationOf"] = "isAnnotationOf";
	propertiesMap["http://example.org/qudex/isVersionOf"] = "isVersionOf";
	propertiesMap["http://example.org/qudex/isFormatOf"] = "isFormatOf";
	propertiesMap["http://example.org/qudex/isRelatedTo"] = "isRelatedTo";
	propertiesMap["http://example.org/qudex/isReferencedBy"] = "isReferencedBy";
	// Key translate enables you to rename keys (eg: to "label" or "type")
	var keyTranslate = {};
	var params = Exhibit.getAttribute(link,"converterTranslateKeys"); // ex:converterTranslateKeys
	if(params != undefined) { keyTranslate = Exhibit.ExhibitJSONXImporter._params(params); }
	// Add keys
	var keyAdd = {};
	params = Exhibit.getAttribute(link,"converterAddKeys"); // ex:converterAddKeys
	if(params != undefined) { keyAdd = Exhibit.ExhibitJSONXImporter._params(params); }

	// Keep track of types
	var types = new Array();
	for(col in ipJson.head.vars) { types[col] = ""; }
	
	// Walk items
	var intermediateItems = [];
	for(var idx=0;idx<ipJson.results.bindings.length;idx++) {
		var b = ipJson.results.bindings[idx];
		var o = new Object();
		for(key in b) {
			if (key == "o")
				continue;
			// Store value
			if (key == "p") {
				var newKey = propertiesMap[b[key].value];
				o[newKey] = b["o"].value;
				if(types[key]=="") { types[newKey]=type; }
				//console.log(newKey);
			} else {
				// If ex:converterTranslateKeys has an entry for this key, use alternative
				var transKey = (keyTranslate[key] != undefined) ? keyTranslate[key] : key ;
				// Store value
				o[transKey] = b[key].value;
				if(types[key]=="") { types[key]=type; }
				//console.log(key);
			}
		}
		// If ex:converterAddKeys has entries, set them
		for(key in keyAdd) {
			o[key] = keyAdd[key];
		}
		intermediateItems.push(o);
		//console.log(o);
	}
	
	// Merge duplicates : find items with same id, merge properties into arrays
	var itemsById = {};
	for(var idx=0;idx<intermediateItems.length;idx++) {
		var id = intermediateItems[idx].id;
		if(!itemsById[id]) { itemsById[id] = []; }  // Not known? Create array
		itemsById[id].push(intermediateItems[idx]);  // Add
	}
	var opItems = [];
	for(var id in itemsById) {
		var items = itemsById[id];
		if(items.length<=1) {
			// Only one item, store unmerged
			opItems.push(items[0]);
		} else {
			// Multiple items with same id, merge *unique* properties
			var opItem = {};
			for(var idx=0;idx<items.length;idx++) {  // For each item with same id
				var nthItem = items[idx];
				for(var p in nthItem) { // For each property
					if(!opItem[p]) { opItem[p] = []; } // Create prop as array
					var cnt=0;
					for(var z=0;z<opItem[p].length;z++) { cnt += (opItem[p][z]==nthItem[p]) ? 1 : 0; }
					if(!cnt) { opItem[p].push(nthItem[p]); } // Copy if not already
				}
			}
			// Turn all single sized arrays into scalars (not strictly necessary, but neater)
			for(var p in opItem) {
				if(opItem[p].length==1) { opItem[p] = opItem[p][0]; }
			}
			opItems.push(opItem);
		}
	}
	
	var opJson = { items: opItems };
	console.log(ipJson,opJson);
	return opJson;
};

Exhibit.ExhibitJSONXImporter._params =  function(str) {
	var result = {};
	var arr = str.split('&');
	for(var i=0;i<arr.length;i++) {
		var p = arr[i].split('=');
		if(p.length==2) {
			result[p[0]] = p[1];
		}
	}
	return result;
};